package com.htgd.gardenexpo.utils;

import javax.crypto.*;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.spec.AlgorithmParameterSpec;

/**
 * @author ：hejing
 * @date ：Created in 2021/8/9 17:56
 * @description：票务加密工具
 * @modified By：`
 * @version: 1.0
 */

public class DesPKCS7Encrypter {
    Cipher ecipher;
    Cipher dcipher;

    public DesPKCS7Encrypter(byte[] keyBytes, byte[] ivBytes)
            throws Exception
    {
        Init(keyBytes,ivBytes);
    }


    DesPKCS7Encrypter(DESKeySpec keySpec, IvParameterSpec ivSpec)
            throws Exception
    {
        Init(keySpec,ivSpec);
    }
    private void Init(byte[] keyBytes, byte[] ivBytes)
            throws Exception
    {
        DESKeySpec dks = new DESKeySpec(keyBytes);
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "DES" );
        SecretKey key = keyFactory.generateSecret( dks );
        IvParameterSpec iv = new IvParameterSpec(ivBytes);
        Init(key,iv);

    }

    private void Init(DESKeySpec keySpec, IvParameterSpec iv)
            throws Exception
    {
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "DES" );
        SecretKey key = keyFactory.generateSecret( keySpec );
        Init(key,iv);
    }

    private void Init(SecretKey key, IvParameterSpec iv)
            throws Exception
    {
        AlgorithmParameterSpec paramSpec = iv;
        try {
            ecipher = Cipher.getInstance("DES/ECB/NoPadding");
            dcipher = Cipher.getInstance("DES/ECB/NoPadding");

            // CBC requires an initialization vector
            ecipher.init(Cipher.ENCRYPT_MODE, key);
            dcipher.init(Cipher.DECRYPT_MODE, key);
        }
        catch (Exception e) {
            throw e;
        }

    }


    public void encrypt(InputStream in, OutputStream out) {
        try {
            // Bytes written to out will be encrypted
            out = new CipherOutputStream(out, ecipher);
            byte[] buf = new byte[this.ecipher.getBlockSize()];

            // Read in the cleartext bytes and write to out to encrypt
            int numRead = 0;
            while (true)
            {
                numRead = in.read(buf);
                boolean bBreak = false;
                if(numRead == -1 || numRead < buf.length)
                {
                    int pos = numRead == -1? 0:numRead;
                    byte byteFill = (byte)(buf.length - pos);
                    for(int i = pos;i<buf.length; ++i)
                    {
                        buf[i] = byteFill;
                    }
                    bBreak = true;
                }
                out.write(buf);

                if(bBreak)
                    break;
            }
            out.close();
        } catch (java.io.IOException e) {
            System.out.println("Exception e in encrypt="+e);
        }
    }

    public void decrypt(InputStream in, OutputStream out) {
        try {
            // Bytes read from in will be decrypted
            in = new CipherInputStream(in, dcipher);
            byte[] buf = new byte[this.dcipher.getBlockSize()];

            // Read in the decrypted bytes and write the cleartext to out
            int numRead = 0;
            while ((numRead = in.read(buf)) >= 0) {
                if(in.available() > 0)
                {
                    out.write(buf, 0, numRead);
                }
                else
                {
                    byte byteBlock = buf[buf.length -1];
                    int i = 0;
                    for(i = buf.length - byteBlock; i >= 0 && i < buf.length; ++i)
                    {
                        if(buf[i] != byteBlock)
                        {
                            break;
                        }
                    }

                    if(i == buf.length)
                    {
                        out.write(buf,0,buf.length - byteBlock);
                    }
                    else
                    {
                        out.write(buf);
                    }
                }
            }

            out.close();
        } catch (java.io.IOException e) {
            System.out.println("Exception e in decrypt="+e);
        }
    }
}
